定期ミートアップ 第6回 yhara
Rubyっぽいフィーリングを持った静的型付き言語
enumを実装したが、パターンマッチがないとメソッドが書けない
パターンマッチの文法を考える
順序による指定と、名前による指定がある
例:class Point; def init(@x: Int, @y: Int) のようなクラスがあるとき
順序による指定
code:sk
def foo(p0: Point, p1: Point)
match p0
when Point(x0, y0)
名前による指定
code:sk
def foo(p0: Point, p1: Point)
match p0
when Point(x, y)
when Point(y, x) # 順番を入れ替えてもOK
when Point(x0, y0) # これはエラー
..
when Point(x: x0, y: y0) # 別名にしたいときは、追加の記法が必要になる
順序による指定:タイプ数が少ない
名前による指定:「順序を覚える必要がない」
Rustの場合
structの定義時にメンバーに名前を付けるかどうかを選べる
code:rs
# 名前なし
struct Point(usize, usize)
# 名前あり
struct Point {
x: usize,
y: usize,
}
名前なしの場合、順序による指定になる
code:rs
match src {
Point(x, y) => { ... }, # OK
Point(x0, y0) => { ... }, # OK
名前ありの場合、名前による指定になる
code:rs
match src {
Point { x, y } => { ... } # OK
Point { y, x } => { ... } # OK
Point { x0, y0 } => { ... } # NG
Point { x: x0, y: y0 } => { ... } # OK
Shiikaではどちらにすればよいか?
キーワード引数
Rubyのそれとは違う
すべての引数はキーワード引数扱い
例
code:sk
class A
def foo(x: Int, y: Int, z: Int, u: Int = 1, v: Int = 2)
...
end
end
...
a.foo(1, 2, 3) # 順序による指定
a.foo(x: 1, y: 2, z: 3) # 名前による指定
a.foo(1, 2, z: 3) # 混合
a.foo(1, 2, 3, v: 2)
混乱を防ぐため、パターンマッチもこれと一貫性があるようにしたい
code:sk
match src
when Point(x, y) # 順序による指定
when Point(x0, y0) # 順序による指定になる
when Point(y, x) # 順序による指定に「なってしまう」
# !?
when Point(x: x0, y: y0) # 名前による指定
when Point(x, y: y0) # 混合
パターンマッチの実装
方針
とりあえず動く(正しい値が出る)ようにしたい
既存の構文に展開するのはどうだろう
実行性能は問わない(実装の単純さを優先)
if-thenのネストに展開
必要なもの
オブジェクトの型を実行時に検査できる
xがPointのインスタンスであること→x.class == Point
ローカル変数スコープの導入(マッチ結果はthen節でのみ使えてほしいので)
code:sk
match src
when Point(x, y)
# ↓
if src.class == Point
fn(x: Int, y: Int){
}(src.x, src.y)
else
if ...
余談
code:rb
def foo(a, b=1, c) # できるらしい
def foo(a, *b, c) # できる
フィードバック
Rustは()と{}の違いがあるが、それなしだと順序を間違えたときに困らないか
それはそうかもしれない
class Foo(@idx: Int, @len: Int) みたいなデータを考える
when Foo(len, idx) と書いたとき、意図せず逆に束縛されてしまう
このパターンは拒絶したほうがいいかも(別名を付けるときは:が必要ということにする)